package iosu.java.games.razer;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import iosu.java.games.razer.api.Client;
import iosu.java.games.razer.api.Game;
import iosu.java.games.razer.api.protocol.RazerProtocolBinarySocket;
import iosu.java.games.razer.server.IRazerServer;
import iosu.java.games.razer.server.RazerServer;
import iosu.java.games.razer.server.ServerResponse;
import iosu.java.games.razer.server.ServerState;

public class FakeRazerServer implements IRazerServer,IFakeRazerServer, Runnable{
	private static final int PORT=9911;
	private  ServerSocketChannel ss;
	private static Logger logger;
	public int fakestatus=0;
	public boolean contestado = false;
	
	static{
		logger= LoggerFactory.getLogger(FakeRazerServer.class);
	}
	
	public FakeRazerServer(){
		try{
			this.ss = ServerSocketChannel.open();
			ss.configureBlocking(false);
			ss.socket().bind(new InetSocketAddress(PORT));
		}catch(IOException e){
			
		}
		
	}

	@Override
	public void run() {
	try {
		Selector selector = Selector.open();
		ss.register(selector, ss.validOps());
		logger.info("FakeRazerServer: escuchando en {}", PORT);
		while(true){
			// wait for events
			selector.select();
			// get keys
			Iterator<SelectionKey> i = selector.selectedKeys().iterator();
			// loop over keys
			while (i.hasNext()) {
				SelectionKey key = i.next();
				// remove current key
				i.remove();
				if (!key.isValid()){
					// si la clave no es valida, paso a la siguiente
					continue;
				}else{
					// si la clave es valida, la proceso
						if(key.isAcceptable()){
						// acepto la conexion
						try{
							this.acceptConn(selector);
						}catch(IOException e){
							System.err.println(e.toString());
							continue;
						}
					}else if(key.isReadable()){
						contestado = false;
						ByteBuffer b = read(key);
						if(fakestatus==0){
							throw new IllegalStateException("no se ha indicado el fakestatus!");
						}
						if(fakestatus==FakeRazerServer.RETURNHELLOOK){
							System.out.println("contestando estado RETURNHELLOOK");
							// retorno hello indicando que hay 2 clientes en la partida
							RazerProtocolBinarySocket.send(RazerProtocolBinarySocket.createHelloResponse(2),key);
							contestado = true;
						}else if(fakestatus==FakeRazerServer.RETURNCREATEGAMEOK){
							RazerProtocolBinarySocket.send(RazerProtocolBinarySocket.createCreateGameResponse(1),key);
							contestado=true;
						}else if(fakestatus==FakeRazerServer.RETURNNOGAMETOJOIN){
							RazerProtocolBinarySocket.send(RazerProtocolBinarySocket.createNoGameToJoinResponse(),key);
							contestado=true;
						}
												
					}else if(key.isWritable()){
					}
				}
			} // while
		} // while (true)
	} catch (Exception e) {
		logger.error("Error en el bucle de escucha",e);
	}
}
	
	private ByteBuffer read(SelectionKey key) throws IOException{
		SocketChannel socketChannel = (SocketChannel) key.channel();
		ByteBuffer readBuffer = ByteBuffer.allocate(512);
		
	    // Clear out our read buffer so it's ready for new data
	    readBuffer.clear();
	    // Attempt to read off the channel
	    int numRead=0;
	    try {
	      numRead = socketChannel.read(readBuffer);
	      logger.debug("read: esperando {} bytes en el buffer",numRead);
	    } catch (IOException e) {
	      // The remote forcibly closed the connection, cancel
	      // the selection key and close the channel.
	      key.cancel();
	      socketChannel.close();
	      return null;
	    }

	    if (numRead == -1) {
	      // Remote entity shut the socket down cleanly. Do the
	      // same from our end and cancel the channel.
	      key.channel().close();
	      key.cancel();
	      return null;
	    }
	    readBuffer.flip();
	    byte[] effectiveRead = new byte[numRead];
	    readBuffer.get(effectiveRead,0,numRead);
	    return ByteBuffer.wrap(effectiveRead);
	}

	private SocketChannel acceptConn(Selector selector) throws IOException{
		// get client socket channel
		SocketChannel client = ss.accept();
		if(client!=null){
			client.configureBlocking(false);
			client.register(selector, SelectionKey.OP_READ);
			logger.info("Client connection from {}",  client.getRemoteAddress( ));
		}
		return client;

	}
	
	@Override
	public void shutdown() {
		try{
			this.ss.close();
		}catch(IOException e){

		}
		
	}

	public int getFakestatus() {
		return fakestatus;
	}

	public void setFakestatus(int fakestatus) {
		logger.info("poniendo fakestatus a "+fakestatus);
		this.fakestatus = fakestatus;
	}

	public boolean getContestado() {
		return contestado;
	}

	public void setContestado(boolean contestado) {
		this.contestado = contestado;
	}

}
